/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.buffer;

import com.aduib.boot.common.exception.IoException;
import com.aduib.boot.common.util.CharsetUtils;
import com.aduib.boot.common.util.ExceptionUtils;
import com.aduib.boot.common.util.StrUtils;
import com.aduib.boot.common.util.SystemUtils;
import sun.nio.ch.DirectBuffer;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.ByteChannel;
import java.nio.charset.Charset;

/**
 * @description: Buffer
 * @author: zzh
 * @date: 2021/11/9 14:10
 */
class Buffer extends AbstractReferenceCounted implements BufferedSource, BufferedSink, ByteChannel {
  // 数据buffer
  private ByteBuffer byteBuffer;

  private int capacity = 8192;

  /** 对应byteBuffer的position */
  private int position = 0;

  /** 对应byteBuffer的limit */
  private int limit = 0;

  /** 当读取数据时开始读取的位置 */
  private int readerIndex;

  /** 当写入数据时开始写入的位置 */
  private int writerIndex;

  /** 读取数据的终点 */
  private int readerLimit;

  /** 写入数据的终点 */
  private int writerLimit;

  /** 是否标记过读取索引 */
  private boolean isMarkReaderIndex = false;

  /** 是否标记过写入索引 */
  private boolean isMarkWriterIndex = false;

  /** 被标记过的读取索引 */
  private int markReaderIndex = 0;

  /** 被标记的写入索引 */
  private int markWriterIndex = 0;

  /** 读写模式，默认时写模式 */
  private MODE mode = MODE.WRITE;

  /** 是否是直接缓冲区 */
  private boolean isDirect = true;

  /** 是否只读 */
  private boolean isReadOnly = false;

  public Buffer(int maxCapacity) {
    this.capacity = maxCapacity;
  }

  public Buffer() {}

  protected Buffer position(int newPosition) {
    byteBuffer.position(newPosition);
    updateReaderIndex();
    updateWriterIndex();
    return this;
  }

  protected int position() {
    return position;
  }

  protected Buffer limit(int newLimit) {
    byteBuffer.limit(newLimit);
    updateReaderIndex();
    updateWriterIndex();
    return this;
  }

  protected int limit() {
    return limit;
  }

  public ByteBuffer getByteBuffer() {
    return this.byteBuffer;
  }

  /**
   * 获取内存地址
   *
   * @return
   */
  public long address() {
    return isDirect ? SystemUtils.directBufferAddress(byteBuffer) : -1L;
  }

  /**
   * 返回次buffer的容量
   *
   * @return
   */
  public int capacity() {
    return capacity;
  }

  void newCapacity(int capacity) {
    this.capacity = capacity;
  }

  /**
   * 剩余容量
   *
   * @return
   */
  public int remaining() {
    return this.byteBuffer.remaining();
  }

  public boolean hasRemaining() {
    return this.byteBuffer.hasRemaining();
  }

  /**
   * 给定指定容量创建buffer，该buffer是直接内存
   *
   * @param capacity
   * @return
   */
  public Buffer capacity(int capacity) {
    capacity(capacity, true);
    return this;
  }

  /**
   * 给定指定容量创建buffer
   *
   * @param capacity
   * @param isDirect
   * @return
   */
  public Buffer capacity(int capacity, boolean isDirect) {
    final ByteBuffer localBuffer;
    if (isDirect) {
      localBuffer = BufferUtils.allocateDirect(capacity);
    } else {
      localBuffer = BufferUtils.allocate(capacity);
    }
    capacity(localBuffer);
    return this;
  }

  /**
   * 通过一个ByteBuffer实例初始化buffer 索引与原来的buffer一致
   *
   * @param byteBuffer
   * @return
   */
  public Buffer capacity(ByteBuffer byteBuffer) {
    this.isDirect = byteBuffer.isDirect();
    this.capacity = byteBuffer.capacity();
    this.isReadOnly = byteBuffer.isReadOnly();
    this.position = byteBuffer.position();
    this.limit = byteBuffer.limit();
    this.byteBuffer = byteBuffer;
    // 当前buffer是读模式
    if (capacity != byteBuffer.limit()) {
      this.mode = MODE.READ;
      this.readerIndex = byteBuffer.position();
      this.readerLimit = byteBuffer.limit();
      this.writerIndex = this.readerLimit;
      this.writerLimit = capacity;
      // 数据读取完毕，重置索引，便于buffer可以重新读
      if (this.position == this.limit) {
        this.byteBuffer.position(0);
        updateReaderIndex();
      }
    } else {
      this.mode = MODE.WRITE;
      this.writerIndex = byteBuffer.position();
      this.writerLimit = byteBuffer.limit();
      this.readerIndex = 0;
      this.readerLimit = writerIndex;
    }
    if (this.isReadOnly()) {
      this.mode = MODE.READ;
      this.writerIndex = 0;
      this.writerLimit = this.capacity;
      this.readerIndex = 0;
      this.readerLimit = this.position;
    }
    return this;
  }

  public boolean isDirect() {
    return isDirect;
  }

  public ByteOrder order() {
    return this.byteBuffer.order();
  }

  public MODE getMode() {
    return mode;
  }

  public boolean isReadOnly() {
    return isReadOnly;
  }

  /** 变成只读Buffer */
  public Buffer asReadOnly() {
    this.byteBuffer = this.byteBuffer.asReadOnlyBuffer();
    Buffer buffer = capacity(byteBuffer);
    buffer.mode = MODE.READ;
    buffer.isReadOnly = true;
    return buffer;
  }

  /**
   * 是否可读
   *
   * @return
   */
  public boolean isReadable() {
    if (!mode.equals(MODE.READ)) {
      return false;
    }
    if (readerIndex > capacity) {
      return false;
    }
    if (readerLimit > capacity) {
      return false;
    }
    return true;
  }

  /**
   * 给定一个数量判断buffer是否可读
   *
   * @param size
   * @return
   */
  public boolean isReadable(int size) {
    if (!isReadable()) {
      return false;
    }
    return capacity - readerIndex > size;
  }

  /**
   * 给定一个数量判断buffer是否可写
   *
   * @param size
   * @return
   */
  public boolean isWriteable(int size) {
    if (!isWriteable()) {
      return false;
    }
    return capacity - writerIndex > size;
  }

  /**
   * 是否可写
   *
   * @return
   */
  public boolean isWriteable() {
    if (!mode.equals(MODE.WRITE)) {
      return false;
    }
    if (writerLimit > capacity) {
      return false;
    }
    if (writerIndex > capacity) {
      return false;
    }
    return true;
  }

  /**
   * 标记读取索引
   *
   * @return
   */
  public Buffer markReaderIndex() {
    doMarKOrResetIndex();
    return this;
  }

  /**
   * 恢复读取索引
   *
   * @return
   */
  public Buffer resetReaderIndex() {
    doMarKOrResetIndex();
    return this;
  }

  /** 标记写入索引 */
  public Buffer markWriterIndex() {
    doMarKOrResetIndex();
    return this;
  }

  /**
   * 恢复写入索引
   *
   * @return
   */
  public Buffer resetWriterIndex() {
    doMarKOrResetIndex();
    return this;
  }

  public int readerIndex() {
    return readerIndex;
  }

  public int writerIndex() {
    return writerIndex;
  }

  public int readableBytes() {
    return writerIndex - readerIndex;
  }

  public int writableBytes() {
    return capacity() - writerIndex;
  }

  public int readerLimit() {
    return readerLimit;
  }

  public int writerLimit() {
    return writerLimit;
  }

  public Buffer attachment() {
    ByteBuffer byteBuffer = (ByteBuffer) (((DirectBuffer) this.byteBuffer).attachment());
    return new Buffer().capacity(byteBuffer);
  }

  public void clear() {
    this.byteBuffer.clear();
    updateWriterIndex();
    updateReaderIndex();
  }

  /** @return The new byte buffer */
  public Buffer slice() {
    ByteBuffer slice = byteBuffer.slice();
    return new Buffer().capacity(slice);
  }

  /**
   * Creates a new byte buffer that shares this buffer's content.
   *
   * <p>The content of the new buffer will be that of this buffer. Changes to this buffer's content
   * will be visible in the new buffer, and vice versa; the two buffers' position, limit, and mark
   * values will be independent.
   *
   * <p>The new buffer's capacity, limit, position, and mark values will be identical to those of
   * this buffer. The new buffer will be direct if, and only if, this buffer is direct, and it will
   * be read-only if, and only if, this buffer is read-only.
   *
   * @return The new byte buffer
   */
  public Buffer duplicate() {
    retain();
    ByteBuffer duplicate = byteBuffer.duplicate();
    return new Buffer().capacity(duplicate);
  }

  /**
   * Creates a new, read-only byte buffer that shares this buffer's content.
   *
   * <p>The content of the new buffer will be that of this buffer. Changes to this buffer's content
   * will be visible in the new buffer; the new buffer itself, however, will be read-only and will
   * not allow the shared content to be modified. The two buffers' position, limit, and mark values
   * will be independent.
   *
   * <p>The new buffer's capacity, limit, position, and mark values will be identical to those of
   * this buffer.
   *
   * <p>If this buffer is itself read-only then this method behaves in exactly the same way as the
   * {@link #duplicate duplicate} method.
   *
   * @return The new, read-only byte buffer
   */
  public Buffer asReadOnlyBuffer() {
    return new Buffer().capacity(byteBuffer.asReadOnlyBuffer());
  }

  // -- Singleton get/put methods --

  /**
   * Relative <i>get</i> method. Reads the byte at this buffer's current position, and then
   * increments the position.
   *
   * @return The byte at the buffer's current position
   * @throws BufferUnderflowException If the buffer's current position is not smaller than its limit
   */
  public byte get() {
    switchReadMode();
    byte b = byteBuffer.get();
    updateReaderIndex();
    return b;
  }

  /**
   * Relative <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes the given byte into this buffer at the current position, and then increments the
   * position.
   *
   * @param b The byte to be written
   * @return This buffer
   * @throws BufferOverflowException If this buffer's current position is not smaller than its limit
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer put(byte b) {
    switchWriteMode();
    byteBuffer.put(b);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method. Reads the byte at the given index.
   *
   * @param index The index from which the byte will be read
   * @return The byte at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit
   */
  public byte get(int index) {
    switchReadMode();
    byte b = byteBuffer.get(index);
    updateReaderIndex();
    return b;
  }
  /**
   * Absolute <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes the given byte into this buffer at the given index.
   *
   * @param index The index at which the byte will be written
   * @param b The byte value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer put(int index, byte b) {
    switchWriteMode();
    byteBuffer.put(index, b);
    updateWriterIndex();
    return this;
  }

  // -- Bulk get operations --

  /**
   * Relative bulk <i>get</i> method.
   *
   * <p>This method transfers bytes from this buffer into the given destination array. If there are
   * fewer bytes remaining in the buffer than are required to satisfy the request, that is, if
   * <tt>length</tt>&nbsp;<tt>&gt;</tt>&nbsp;<tt>remaining()</tt>, then no bytes are transferred and
   * a {@link BufferUnderflowException} is thrown.
   *
   * <p>Otherwise, this method copies <tt>length</tt> bytes from this buffer into the given array,
   * starting at the current position of this buffer and at the given offset in the array. The
   * position of this buffer is then incremented by <tt>length</tt>.
   *
   * <p>In other words, an invocation of this method of the form
   * <tt>src.get(dst,&nbsp;off,&nbsp;len)</tt> has exactly the same effect as the loop
   *
   * <pre>{@code
   * for (int i = off; i < off + len; i++)
   *     dst[i] = src.get():
   * }</pre>
   *
   * except that it first checks that there are sufficient bytes in this buffer and it is
   * potentially much more efficient.
   *
   * @param dst The array into which bytes are to be written
   * @param offset The offset within the array of the first byte to be written; must be non-negative
   *     and no larger than <tt>dst.length</tt>
   * @param length The maximum number of bytes to be written to the given array; must be
   *     non-negative and no larger than <tt>dst.length - offset</tt>
   * @return This buffer
   * @throws BufferUnderflowException If there are fewer than <tt>length</tt> bytes remaining in
   *     this buffer
   * @throws IndexOutOfBoundsException If the preconditions on the <tt>offset</tt> and
   *     <tt>length</tt> parameters do not hold
   */
  public Buffer get(byte[] dst, int offset, int length) {
    switchReadMode();
    this.byteBuffer.get(dst, offset, length);
    updateReaderIndex();
    return this;
  }

  /**
   * Relative bulk <i>get</i> method.
   *
   * <p>This method transfers bytes from this buffer into the given destination array. An invocation
   * of this method of the form <tt>src.get(a)</tt> behaves in exactly the same way as the
   * invocation
   *
   * <pre>
   *     src.get(a, 0, a.length) </pre>
   *
   * @param dst The destination array
   * @return This buffer
   * @throws BufferUnderflowException If there are fewer than <tt>length</tt> bytes remaining in
   *     this buffer
   */
  public Buffer get(byte[] dst) {
    return get(dst, 0, dst.length);
  }

  // -- Bulk put operations --

  /**
   * Relative bulk <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>This method transfers the bytes remaining in the given source buffer into this buffer. If
   * there are more bytes remaining in the source buffer than in this buffer, that is, if
   * <tt>src.remaining()</tt>&nbsp;<tt>&gt;</tt>&nbsp;<tt>remaining()</tt>, then no bytes are
   * transferred and a {@link BufferOverflowException} is thrown.
   *
   * <p>Otherwise, this method copies <i>n</i>&nbsp;=&nbsp;<tt>src.remaining()</tt> bytes from the
   * given buffer into this buffer, starting at each buffer's current position. The positions of
   * both buffers are then incremented by <i>n</i>.
   *
   * <p>In other words, an invocation of this method of the form <tt>dst.put(src)</tt> has exactly
   * the same effect as the loop
   *
   * <pre>
   *     while (src.hasRemaining())
   *         dst.put(src.get()); </pre>
   *
   * except that it first checks that there is sufficient space in this buffer and it is potentially
   * much more efficient.
   *
   * @param src The source buffer from which bytes are to be read; must not be this buffer
   * @return This buffer
   * @throws BufferOverflowException If there is insufficient space in this buffer for the remaining
   *     bytes in the source buffer
   * @throws IllegalArgumentException If the source buffer is this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer put(Buffer src) {
    src.switchReadMode();
    switchWriteMode();
    this.byteBuffer.put(src.byteBuffer);
    updateWriterIndex();
    src.updateReaderIndex();
    return this;
  }

  public Buffer put(ByteBuffer src) {
    switchWriteMode();
    this.byteBuffer.put(src);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative bulk <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>This method transfers bytes into this buffer from the given source array. If there are more
   * bytes to be copied from the array than remain in this buffer, that is, if
   * <tt>length</tt>&nbsp;<tt>&gt;</tt>&nbsp;<tt>remaining()</tt>, then no bytes are transferred and
   * a {@link BufferOverflowException} is thrown.
   *
   * <p>Otherwise, this method copies <tt>length</tt> bytes from the given array into this buffer,
   * starting at the given offset in the array and at the current position of this buffer. The
   * position of this buffer is then incremented by <tt>length</tt>.
   *
   * <p>In other words, an invocation of this method of the form
   * <tt>dst.put(src,&nbsp;off,&nbsp;len)</tt> has exactly the same effect as the loop
   *
   * <pre>{@code
   * for (int i = off; i < off + len; i++)
   *     dst.put(a[i]);
   * }</pre>
   *
   * except that it first checks that there is sufficient space in this buffer and it is potentially
   * much more efficient.
   *
   * @param src The array from which bytes are to be read
   * @param offset The offset within the array of the first byte to be read; must be non-negative
   *     and no larger than <tt>array.length</tt>
   * @param length The number of bytes to be read from the given array; must be non-negative and no
   *     larger than <tt>array.length - offset</tt>
   * @return This buffer
   * @throws BufferOverflowException If there is insufficient space in this buffer
   * @throws IndexOutOfBoundsException If the preconditions on the <tt>offset</tt> and
   *     <tt>length</tt> parameters do not hold
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer put(byte[] src, int offset, int length) {
    switchWriteMode();
    this.byteBuffer.put(src, offset, length);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative bulk <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>This method transfers the entire content of the given source byte array into this buffer. An
   * invocation of this method of the form <tt>dst.put(a)</tt> behaves in exactly the same way as
   * the invocation
   *
   * <pre>
   *     dst.put(a, 0, a.length) </pre>
   *
   * @param src The source array
   * @return This buffer
   * @throws BufferOverflowException If there is insufficient space in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public final Buffer put(byte[] src) {
    return put(src, 0, src.length);
  }

  /**
   * Returns the byte array that backs this buffer&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Modifications to this buffer's content will cause the returned array's content to be
   * modified, and vice versa.
   *
   * <p>Invoke the method before invoking this method in order to ensure that this buffer has an
   * accessible backing array.
   *
   * @return The array that backs this buffer
   * @throws ReadOnlyBufferException If this buffer is backed by an array but is read-only
   * @throws UnsupportedOperationException If this buffer is not backed by an accessible array
   */
  public final byte[] array() {
    return this.byteBuffer.array();
  }

  /**
   * Compacts this buffer&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>The bytes between the buffer's current position and its limit, if any, are copied to the
   * beginning of the buffer. That is, the byte at index <i>p</i>&nbsp;=&nbsp;<tt>position()</tt> is
   * copied to index zero, the byte at index <i>p</i>&nbsp;+&nbsp;1 is copied to index one, and so
   * forth until the byte at index <tt>limit()</tt>&nbsp;-&nbsp;1 is copied to index
   * <i>n</i>&nbsp;=&nbsp;<tt>limit()</tt>&nbsp;-&nbsp;<tt>1</tt>&nbsp;-&nbsp;<i>p</i>. The buffer's
   * position is then set to <i>n+1</i> and its limit is set to its capacity. The mark, if defined,
   * is discarded.
   *
   * <p>The buffer's position is set to the number of bytes copied, rather than to zero, so that an
   * invocation of this method can be followed immediately by an invocation of another relative
   * <i>put</i> method.
   *
   * <p>Invoke this method after writing data from a buffer in case the write was incomplete. The
   * following loop, for example, copies bytes from one channel to another via the buffer
   * <tt>buf</tt>:
   *
   * <blockquote>
   *
   * <pre>{@code
   * buf.clear();          // Prepare buffer for use
   * while (in.read(buf) >= 0 || buf.position != 0) {
   *     buf.flip();
   *     out.write(buf);
   *     buf.compact();    // In case of partial write
   * }
   * }</pre>
   *
   * </blockquote>
   *
   * @return This buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer compact() {
    ByteBuffer compact = byteBuffer.compact();
    capacity(compact);
    return this;
  }

  /**
   * Relative <i>get</i> method for reading a char value.
   *
   * <p>Reads the next two bytes at this buffer's current position, composing them into a char value
   * according to the current byte order, and then increments the position by two.
   *
   * @return The char value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than two bytes remaining in this buffer
   */
  public char getChar() {
    switchReadMode();
    final char value = this.byteBuffer.getChar();
    updateReaderIndex();
    return value;
  }

  /**
   * Relative <i>put</i> method for writing a char value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes two bytes containing the given char value, in the current byte order, into this
   * buffer at the current position, and then increments the position by two.
   *
   * @param value The char value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than two bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putChar(char value) {
    switchWriteMode();
    this.byteBuffer.putChar(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading a char value.
   *
   * <p>Reads two bytes at the given index, composing them into a char value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The char value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus one
   */
  public char getChar(int index) {
    switchReadMode();
    final char value = this.byteBuffer.getChar(index);
    updateReaderIndex();
    return value;
  }

  /**
   * Absolute <i>put</i> method for writing a char value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes two bytes containing the given char value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The char value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus one
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putChar(int index, char value) {
    switchWriteMode();
    this.byteBuffer.putChar(index, value);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative <i>get</i> method for reading a short value.
   *
   * <p>Reads the next two bytes at this buffer's current position, composing them into a short
   * value according to the current byte order, and then increments the position by two.
   *
   * @return The short value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than two bytes remaining in this buffer
   */
  public short getShort() {
    switchReadMode();
    final short value = this.byteBuffer.getShort();
    updateReaderIndex();
    return value;
  }

  /**
   * Relative <i>put</i> method for writing a short value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes two bytes containing the given short value, in the current byte order, into this
   * buffer at the current position, and then increments the position by two.
   *
   * @param value The short value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than two bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putShort(short value) {
    switchWriteMode();
    this.byteBuffer.putShort(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading a short value.
   *
   * <p>Reads two bytes at the given index, composing them into a short value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The short value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus one
   */
  public short getShort(int index) {
    switchReadMode();
    final short value = this.byteBuffer.getShort(index);
    updateReaderIndex();
    return value;
  }

  /**
   * Absolute <i>put</i> method for writing a short value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes two bytes containing the given short value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The short value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus one
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putShort(int index, short value) {
    switchWriteMode();
    this.byteBuffer.putShort(index, value);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative <i>get</i> method for reading an int value.
   *
   * <p>Reads the next four bytes at this buffer's current position, composing them into an int
   * value according to the current byte order, and then increments the position by four.
   *
   * @return The int value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than four bytes remaining in this buffer
   */
  public int getInt() {
    switchReadMode();
    final int value = this.byteBuffer.getInt();
    updateReaderIndex();
    return value;
  }

  /**
   * Relative <i>put</i> method for writing an int value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes four bytes containing the given int value, in the current byte order, into this
   * buffer at the current position, and then increments the position by four.
   *
   * @param value The int value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than four bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putInt(int value) {
    switchWriteMode();
    this.byteBuffer.putInt(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading an int value.
   *
   * <p>Reads four bytes at the given index, composing them into a int value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The int value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus three
   */
  public int getInt(int index) {
    switchReadMode();
    final int value = this.byteBuffer.getInt(index);
    updateReaderIndex();
    return value;
  }

  /**
   * Absolute <i>put</i> method for writing an int value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes four bytes containing the given int value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The int value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus three
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putInt(int index, int value) {
    switchWriteMode();
    this.byteBuffer.putInt(index, value);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative <i>get</i> method for reading a long value.
   *
   * <p>Reads the next eight bytes at this buffer's current position, composing them into a long
   * value according to the current byte order, and then increments the position by eight.
   *
   * @return The long value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than eight bytes remaining in this buffer
   */
  public long getLong() {
    switchReadMode();
    final long value = this.byteBuffer.getLong();
    updateReaderIndex();
    return value;
  }

  /**
   * Relative <i>put</i> method for writing a long value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes eight bytes containing the given long value, in the current byte order, into this
   * buffer at the current position, and then increments the position by eight.
   *
   * @param value The long value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than eight bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putLong(long value) {
    switchWriteMode();
    this.byteBuffer.putLong(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading a long value.
   *
   * <p>Reads eight bytes at the given index, composing them into a long value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The long value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus seven
   */
  public long getLong(int index) {
    switchReadMode();
    final long value = this.byteBuffer.getLong(index);
    updateReaderIndex();
    return value;
  }

  /**
   * Absolute <i>put</i> method for writing a long value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes eight bytes containing the given long value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The long value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus seven
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putLong(int index, long value) {
    switchWriteMode();
    this.byteBuffer.putLong(index, value);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative <i>get</i> method for reading a float value.
   *
   * <p>Reads the next four bytes at this buffer's current position, composing them into a float
   * value according to the current byte order, and then increments the position by four.
   *
   * @return The float value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than four bytes remaining in this buffer
   */
  public float getFloat() {
    switchReadMode();
    final float value = this.byteBuffer.getFloat();
    updateReaderIndex();
    return value;
  }

  /**
   * Relative <i>put</i> method for writing a float value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes four bytes containing the given float value, in the current byte order, into this
   * buffer at the current position, and then increments the position by four.
   *
   * @param value The float value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than four bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putFloat(float value) {
    switchWriteMode();
    this.byteBuffer.putFloat(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading a float value.
   *
   * <p>Reads four bytes at the given index, composing them into a float value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The float value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus three
   */
  public float getFloat(int index) {
    switchReadMode();
    final float value = this.byteBuffer.getFloat(index);
    updateReaderIndex();
    return value;
  }

  /**
   * Absolute <i>put</i> method for writing a float value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes four bytes containing the given float value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The float value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus three
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putFloat(int index, float value) {
    switchWriteMode();
    this.byteBuffer.putFloat(index, value);
    updateWriterIndex();
    return this;
  }

  /**
   * Relative <i>get</i> method for reading a double value.
   *
   * <p>Reads the next eight bytes at this buffer's current position, composing them into a double
   * value according to the current byte order, and then increments the position by eight.
   *
   * @return The double value at the buffer's current position
   * @throws BufferUnderflowException If there are fewer than eight bytes remaining in this buffer
   */
  public double getDouble() {
    switchReadMode();
    final double v = this.byteBuffer.getDouble();
    updateReaderIndex();
    return v;
  }

  /**
   * Relative <i>put</i> method for writing a double value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes eight bytes containing the given double value, in the current byte order, into this
   * buffer at the current position, and then increments the position by eight.
   *
   * @param value The double value to be written
   * @return This buffer
   * @throws BufferOverflowException If there are fewer than eight bytes remaining in this buffer
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putDouble(double value) {
    switchWriteMode();
    this.byteBuffer.putDouble(value);
    updateWriterIndex();
    return this;
  }

  /**
   * Absolute <i>get</i> method for reading a double value.
   *
   * <p>Reads eight bytes at the given index, composing them into a double value according to the
   * current byte order.
   *
   * @param index The index from which the bytes will be read
   * @return The double value at the given index
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus seven
   */
  public double getDouble(int index) {
    switchReadMode();
    final double v = this.byteBuffer.getDouble(index);
    updateReaderIndex();
    return v;
  }

  /**
   * Absolute <i>put</i> method for writing a double value&nbsp;&nbsp;<i>(optional operation)</i>.
   *
   * <p>Writes eight bytes containing the given double value, in the current byte order, into this
   * buffer at the given index.
   *
   * @param index The index at which the bytes will be written
   * @param value The double value to be written
   * @return This buffer
   * @throws IndexOutOfBoundsException If <tt>index</tt> is negative or not smaller than the
   *     buffer's limit, minus seven
   * @throws ReadOnlyBufferException If this buffer is read-only
   */
  public Buffer putDouble(int index, double value) {
    switchWriteMode();
    this.byteBuffer.putDouble(index, value);
    updateWriterIndex();
    return this;
  }

  /** 标记或者恢复索引 */
  private void doMarKOrResetIndex() {
    // 当前是写模式
    if (mode.equals(MODE.WRITE)) {
      // 未标记过写入索引
      if (!isMarkWriterIndex) {
        this.markWriterIndex = writerIndex;
        this.isMarkWriterIndex = true;
        BufferUtils.mark(byteBuffer);
      } else {
        this.isMarkWriterIndex = false;
        BufferUtils.reset(byteBuffer);
        this.markWriterIndex = byteBuffer.position();
      }
    } else {
      if (!isMarkReaderIndex) {
        this.markReaderIndex = readerIndex;
        this.isMarkReaderIndex = true;
        BufferUtils.mark(byteBuffer);
      } else {
        this.isMarkReaderIndex = false;
        BufferUtils.reset(byteBuffer);
        this.markReaderIndex = byteBuffer.position();
      }
    }
  }

  /** 恢复读写索引 */
  private void resumePositionAndLimit() {
    // 当前是写模式
    if (mode.equals(MODE.WRITE)) {
      // 未标记过写入索引
      if (writerIndex > 0) {
        this.byteBuffer.position(writerIndex);
        this.byteBuffer.limit(writerLimit);
        this.writerIndex = position;
        this.writerLimit = limit;
      }
    } else if (mode.equals(MODE.READ)) {
      if (readerIndex > 0) {
        this.byteBuffer.position(readerIndex);
        this.byteBuffer.limit(Math.max(readerLimit, writerLimit));
        this.readerIndex = position;
        this.readerLimit = limit;
      }
    }
  }

  /**
   * 切换读写模式,当前是读模式切换到写模式，写模式切换成读模式
   *
   * @return ${@code true}切换成功，${@code false}切换失败
   */
  @Deprecated
  private boolean switchMode() {
    return false;
  }

  /**
   * 读模式切换到写模式
   *
   * @return ${@code true}切换成功，${@code false}切换失败
   */
  protected boolean switchWriteMode() {
    // 没有写入数据时不用切换
    //    if (writerIndex == writerLimit) {
    //      return false;
    //    }
    // 写模式切换成读模式
    if (mode.equals(MODE.READ)) {
      // 读模式切换到写模式
      BufferUtils.clear(byteBuffer);
      // 设置写模式
      this.mode = MODE.WRITE;
      // 恢复写索引
      resumePositionAndLimit();
      // 更新写索引
      updateWriterIndex();
      return true;
    }
    return false;
  }

  /**
   * 切换读模式
   *
   * @return
   */
  protected boolean switchReadMode() {
    // 没有写入数据时不用切换
    //    if (readerIndex==readerLimit) {
    //      return false;
    //    }
    // 写模式切换成读模式
    if (mode.equals(MODE.WRITE)) {
      // 写模式切换到读模式
      BufferUtils.flip(byteBuffer);
      // 设置读模式
      this.mode = MODE.READ;
      // 恢复读索引
      resumePositionAndLimit();
      // 更新读索引
      updateReaderIndex();
      return true;
    }
    return false;
  }

  /** 更新读索引 */
  protected void updateReaderIndex() {
    if (mode.equals(MODE.READ)) {
      updateReaderIndex(this.byteBuffer);
    }
  }

  /** 更新写索引 */
  protected void updateWriterIndex() {
    if (mode.equals(MODE.WRITE)) {
      updateWriterIndex(this.byteBuffer);
    }
  }

  /** 更新读索引 */
  private void updateReaderIndex(ByteBuffer byteBuffer) {
    this.position = byteBuffer.position();
    this.limit = byteBuffer.limit();
    this.readerIndex = position;
    this.readerLimit = limit;
  }

  /** 更新写索引 */
  private void updateWriterIndex(ByteBuffer byteBuffer) {
    this.position = byteBuffer.position();
    this.limit = byteBuffer.limit();
    this.writerIndex = position;
    this.writerLimit = limit;
  }

  @Override
  public int hashCode() {
    return BufferUtils.hashCode(this);
  }

  @Override
  public boolean equals(Object o) {
    return o instanceof Buffer && BufferUtils.equals(this, (Buffer) o);
  }

  @Override
  public String toString() {
    return "Buffer{"
        + "capacity="
        + capacity
        + ", position="
        + position
        + ", limit="
        + limit
        + ", readerIndex="
        + readerIndex
        + ", readerLimit="
        + readerLimit
        + ", writerIndex="
        + writerIndex
        + ", writerLimit="
        + writerLimit
        + ", isMarkReaderIndex="
        + isMarkReaderIndex
        + ", markReaderIndex="
        + markReaderIndex
        + ", isMarkWriterIndex="
        + isMarkWriterIndex
        + ", markWriterIndex="
        + markWriterIndex
        + ", mode="
        + mode
        + ", isDirect="
        + isDirect
        + ", isReadOnly="
        + isReadOnly
        + '}';
  }

  @Override
  protected void deallocate() {
    BufferUtils.freeBuffer(this);
  }

  /*---------NIO----------**/

  @Override
  public Buffer buffer() {
    return this;
  }

  @Override
  public OutputStream outputStream() {
    return new OutputStream() {
      @Override
      public void write(int i) throws IOException {
        writeByte((byte) i);
      }

      @Override
      public void write(byte[] b, int off, int len) throws IOException {
        Buffer.this.write(b, off, len);
      }

      @Override
      public void flush() throws IOException {}

      @Override
      public void close() throws IOException {}

      @Override
      public String toString() {
        return Buffer.this + ".outputStream()";
      }
    };
  }

  @Override
  public BufferedSink write(byte[] source) throws IoException {
    try {
      put(source);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink write(byte[] source, int offset, int byteCount) throws IoException {
    try {
      put(source, offset, byteCount);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public long writeAll(Source source) throws IoException {
    long totalBytesRead = 0;
    try {
      for (long readCount; (readCount = source.read(this, writableBytes())) != -1; ) {
        totalBytesRead += readCount;
      }
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return totalBytesRead;
  }

  @Override
  public BufferedSink write(Source source, int byteCount) throws IoException {
    if (!request(byteCount)) throw new ArrayIndexOutOfBoundsException(byteCount);
    while (byteCount > 0) {
      try {
        long read = source.read(this, byteCount);
        if (read == -1) throw new EOFException();
        byteCount -= read;
      } catch (Throwable e) {
        ExceptionUtils.wrap(e, IoException.class);
      }
    }
    return this;
  }

  @Override
  public BufferedSink writeStringUtf8(String source) throws IoException {
    writeString(source, CharsetUtils.CHARSET_UTF_8);
    return this;
  }

  @Override
  public BufferedSink writeString(String source, Charset charset) throws IoException {
    try {
      write(StrUtils.bytes(source, charset), 0, source.length());
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeByte(byte b) throws IoException {
    try {
      put(b);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeShort(short s) throws IoException {
    try {
      putShort(s);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeInt(int i) throws IoException {
    try {
      putInt(i);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeLong(long v) throws IoException {
    try {
      putLong(v);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeFloat(float v) throws IoException {
    try {
      putFloat(v);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public BufferedSink writeDouble(double v) throws IoException {
    try {
      putDouble(v);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return this;
  }

  @Override
  public Buffer getBuffer() {
    return this;
  }

  @Override
  public boolean exhausted() throws IoException {
    return readableBytes() == 0;
  }

  @Override
  public boolean request(long byteCount) throws IoException {
    return readableBytes() >= byteCount;
  }

  @Override
  public byte readByte() throws IoException {
    byte b = 0;
    try {
      b = get();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public short readShort() throws IoException {
    short b = 0;
    try {
      b = getShort();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public int readInt() throws IoException {
    int b = 0;
    try {
      b = getInt();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public long readLong() throws IoException {
    long b = 0;
    try {
      b = getLong();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public float readFloat() throws IoException {
    float b = 0f;
    try {
      b = getFloat();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public double readDouble() throws IoException {
    double b = 0d;
    try {
      b = getDouble();
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return b;
  }

  @Override
  public void skip(long byteCount) throws IoException {
    try {
      if (!request(byteCount)) throw new ArrayIndexOutOfBoundsException((int) byteCount);
      position(readerIndex + (int) byteCount);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
  }

  @Override
  public byte[] readByteArray() throws IoException {
    byte[] bytes = new byte[readableBytes()];
    try {
      get(bytes, 0, readableBytes());
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return bytes;
  }

  @Override
  public byte[] readByteArray(int byteCount) throws IoException {
    if (!request(byteCount)) throw new ArrayIndexOutOfBoundsException(byteCount);
    byte[] bytes = new byte[byteCount];
    try {
      get(bytes, 0, byteCount);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return bytes;
  }

  @Override
  public int read(byte[] sink) throws IoException {
    int length = sink.length;
    if (!request(length)) throw new ArrayIndexOutOfBoundsException(length);
    try {
      get(sink, 0, length);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return sink.length;
  }

  @Override
  public void readFully(byte[] sink) throws IoException {
    int length = sink.length;
    if (!request(length)) throw new ArrayIndexOutOfBoundsException(length);
    try {
      get(sink, 0, sink.length);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
  }

  @Override
  public int read(byte[] sink, int offset, int byteCount) throws IoException {
    if (!request(byteCount)) throw new ArrayIndexOutOfBoundsException(byteCount);
    if (offset > readerIndex) {
      throw new ArrayIndexOutOfBoundsException(offset);
    }
    try {
      get(sink, offset, byteCount);
    } catch (Throwable e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return byteCount;
  }

  @Override
  public void readFully(Buffer sink, int byteCount) throws IoException {
    if (!request(byteCount)) throw new ArrayIndexOutOfBoundsException(byteCount);
    try {
      sink.write(this, byteCount);
    } catch (IOException e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
  }

  @Override
  public long readAll(Sink sink) throws IoException {
    int byteCount = readableBytes();
    try {
      sink.write(this, byteCount);
    } catch (IOException e) {
      ExceptionUtils.wrap(e, IoException.class);
    }
    return byteCount;
  }

  @Override
  public String readStringUtf8(int byteCount) throws IoException {
    return readString(byteCount, CharsetUtils.CHARSET_UTF_8);
  }

  @Override
  public String readString(int byteCount, Charset charset) throws IoException {
    return StrUtils.str(readByteArray(byteCount), charset);
  }

  @Override
  public InputStream inputStream() {
    return new InputStream() {
      @Override
      public int read() {
        if (readableBytes() > 0) return readByte() & 0xff;
        return -1;
      }

      @Override
      public int read(byte[] sink, int offset, int byteCount) {
        return Buffer.this.read(sink, offset, byteCount);
      }

      @Override
      public int available() {
        return (int) Math.min(readableBytes(), Integer.MAX_VALUE);
      }

      @Override
      public void close() {}

      @Override
      public String toString() {
        return Buffer.this + ".inputStream()";
      }
    };
  }

  @Override
  public void write(Buffer source, int byteCount) throws IOException {
    source.read(this, byteCount);
  }

  @Override
  public void flush() throws IoException {}

  @Override
  public long read(Buffer sink, int byteCount) throws IOException {
    final byte[] bytes = readByteArray(byteCount);
    sink.write(bytes);
    return byteCount;
  }

  @Override
  public void close() throws IoException {}

  @Override
  public int read(ByteBuffer byteBuffer) throws IoException {
    int readLimit = readableBytes();
    byteBuffer.clear();
    int remaining = byteBuffer.remaining();
    if (readLimit > remaining) {
      throw new IoException("overflow: length: " + (readLimit - remaining));
    }
    final byte[] src = readByteArray(remaining);
    byteBuffer.put(src);
    return src.length;
  }

  @Override
  public int write(ByteBuffer byteBuffer) throws IoException {
    int writeLimit = writableBytes();
    byteBuffer.flip();
    int remaining = byteBuffer.remaining();
    if (writeLimit < remaining) {
      throw new IoException("overflow: length: " + (remaining - writerLimit));
    }
    put(byteBuffer);
    return writeLimit - remaining;
  }

  @Override
  public boolean isOpen() {
    return true;
  }

  /*---------NIO----------**/
  // 读写模式枚举
  enum MODE {
    /** 读 */
    READ,
    /** 写 */
    WRITE
  }
}
